using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

namespace Duhking
{
    public class MethodInfoComparer : IEqualityComparer<MethodInfo>
    {
        readonly InterfaceDuhker _duhker = new InterfaceDuhker();

        public MethodInfoComparer()
        { }

        public MethodInfoComparer(InterfaceDuhker duhker)
        {
            _duhker = duhker;
        }

        public bool Equals(MethodInfo interfaceType, MethodInfo objectType)
        {
            return interfaceType.Name == objectType.Name
                && (interfaceType.ReturnType == objectType.ReturnType || ReturnTypeIsDuhkable(interfaceType, objectType))
                && interfaceType.IsGenericMethodDefinition == objectType.IsGenericMethodDefinition
                && interfaceType.IsGenericMethod == objectType.IsGenericMethod
                && CompareParameters(interfaceType.GetParameters(), objectType.GetParameters());
        }

        private bool CompareParameters(ParameterInfo[] interfaceParameters, ParameterInfo[] objectParameters)
        {
            var interfaceParamTypes = interfaceParameters.Select(p => p.ParameterType);
            var objectParameterTypes = objectParameters.Select(p => p.ParameterType);
            return interfaceParameters.Count() == objectParameters.Count()
                && CompareParameterTypes(interfaceParamTypes,objectParameterTypes);
        }

        public bool CompareParameterTypes(IEnumerable<Type> interfaceParamTypes, IEnumerable<Type> objectParamTypes)
        {
            for (var i = 0; i < interfaceParamTypes.Count(); i++)
            {
                if (!(interfaceParamTypes.ElementAt(i) == objectParamTypes.ElementAt(i) || ParameterTypeIsDuhkable(interfaceParamTypes.ElementAt(i), objectParamTypes.ElementAt(i))))
                    return false;
            }
            return true;
        }

        private bool ReturnTypeIsDuhkable(MethodInfo interfaceType, MethodInfo objectType)
        {
            return interfaceType.ReturnType.IsInterface && _duhker.CanDuhk(interfaceType.ReturnType, objectType.ReturnType);
        }
        private bool ParameterTypeIsDuhkable(Type interfaceParamType, Type objectParamType)
        {
            return interfaceParamType.IsInterface && _duhker.CanDuhk(interfaceParamType, objectParamType);
        }
        public int GetHashCode(MethodInfo obj)
        {
            return obj.GetHashCode();
        }
    }
}
